//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.RegistryManagerModule
{
    using System;
    using System.Collections;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Windows;
    using ManagementStudioServices.Interfaces;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using SR = Microsoft.Research.DataLayer;
    using Microsoft.Research.eResearch.Common.Linq;

    /// <summary>
    /// The left pane presenter for nodes.
    /// </summary>
    public class NodeLeftPanePresenter : IRegistryManagerLeftPaneItemPresenter
    {
        #region Private Members

        /// <summary>
        /// The unity container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Service for registry access.
        /// </summary>
        private IRegistryManagerService registryManagerService;

        /// <summary>
        /// The health service.
        /// </summary>
        private HealthCheck.HealthCheckService healthCheckService;

        /// <summary>
        /// The parent node model.
        /// </summary>
        private NodeViewModel baseModel;

        #endregion Private Members

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="NodeLeftPanePresenter"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="leftPane">The left pane.</param>
        /// <param name="service">The service.</param>
        public NodeLeftPanePresenter(IUnityContainer container, NodeEditorLeftPaneView leftPane, IRegistryManagerService service)
         {
            this.container = container;
            this.LeftPaneView = leftPane;
            this.registryManagerService = service;
            leftPane.DataContext = this;
            this.LeftPaneTreeItems = new ObservableCollection<NodeViewModel>();
            this.NodeSelectedCommand = new DelegateCommand<NodeViewModel>(this.NodeSelectedCommandExecute);

            this.healthCheckService = this.container.Resolve<HealthCheck.HealthCheckService>();
            this.healthCheckService.HealthCheckEvent += this.OnHealthCheckServiceHealthCheckEvent;
        }

        #endregion Constructor

        #region IRegistryManagerLeftPaneItemPresenter Members

        /// <summary>
        /// Flag to determine whether the presenter is currently selected.
        /// </summary>
        /// <value></value>
        public bool IsSelected
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the search view.
        /// </summary>
        /// <value>The search view.</value>
        public UIElement SearchView
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the left pane view.
        /// </summary>
        /// <value>The left pane view.</value>
        public UIElement LeftPaneView
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the left pane presener.
        /// </summary>
        /// <value>The left pane presener.</value>
        public ILeftPaneControl LeftPanePresenter
        {
            get;
            set;
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        public void Initialize()
        {
            this.LeftPaneTreeItems.Clear();
            this.LoadNodes();
        }

        /// <summary>
        /// Refreshes this instance.
        /// </summary>
        public void Refresh(BaseRegMgrModel selectedModel)
        {
            this.Initialize();
            if (selectedModel != null)
            {
                if (selectedModel is NodeViewModel)
                {
                    BaseRegMgrModel modelToSelect = selectedModel.GetModelById(selectedModel, this.LeftPaneTreeItems.Cast<BaseRegMgrModel>());
                    if (modelToSelect != null)
                    {
                        this.NodeSelectedCommand.Execute(modelToSelect as NodeViewModel);
                    }
                }
                else
                {
                    this.OnSelectRoot();
                }
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// The list of node models.
        /// </summary>
        public ObservableCollection<NodeViewModel> LeftPaneTreeItems { get; private set; }

        #endregion Properties

        #region Commands

        /// <summary>
        /// The command for the node selected operation.
        /// </summary>
        public DelegateCommand<NodeViewModel> NodeSelectedCommand { get; private set; }

        #endregion

        #region Methods

        /// <summary>
        /// Called when health check service health check event is raised.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="Microsoft.Research.ScientificWorkflow.ManagementStudioControls.HealthCheck.HealthCheckEventArgs"/> instance containing the event data.</param>
        private void OnHealthCheckServiceHealthCheckEvent(object sender, HealthCheck.HealthCheckEventArgs e)
        {
             this.LeftPaneView.Dispatcher.BeginInvoke((Action)(() => { this.UpdateStatus(e.healthCheckData); }));
        }

        /// <summary>
        /// Update the status for the nodes.
        /// </summary>
        /// <param name="statusDictionary"></param>
        public void UpdateStatus(Hashtable statusDictionary)
        {
            if (this.baseModel != null)
            {
                // The hash contains only the faulty nodes. So set all nodes to running prior to setting them to faulty.
                foreach (NodeViewModel node in this.baseModel.Children)
                {
                    node.MachineStatus = Microsoft.Research.DataLayer.MachineStatus.Running;
                    node.SchedulerStatus = Microsoft.Research.DataLayer.SchedulerStatus.Running;
                }
                foreach (Guid guid in statusDictionary.Keys)
                {
                    NodeViewModel nodeToUpdate = this.baseModel.Children.OfType<NodeViewModel>().FirstOrDefault(p => p.Id == guid);
                    if (nodeToUpdate != null)
                    {
                        nodeToUpdate.MachineStatus = ((SR.Machine)statusDictionary[guid]).Status;
                        nodeToUpdate.SchedulerStatus = ((SR.Machine)statusDictionary[guid]).SchedulerStatus;
                    }
                }
            }
        }

        /// <summary>
        /// Load the nodes from the registry.
        /// </summary>
        private void LoadNodes()
        {
            try
            {
                baseModel = new NodeViewModel("Nodes", string.Empty);
                this.LeftPaneTreeItems.Add(baseModel);

                // Get the collection of all available machines.
                Collection<SR.Machine> machineList = this.registryManagerService.GetNodes();
                if (machineList != null)
                {
                    // HPCComputeNodes are non-executors. Iterate through all other nodes.
                    foreach (SR.Machine executor in machineList.Where(M => M.Type != SR.MachineType.HPCComputeNode))
                    {
                        NodeViewModel nodeModel = new NodeViewModel(executor);

                        // Find out all the child nodes of this executor.
                        foreach (SR.Machine machine in machineList)
                        {
                            if (machine.Parent != null)
                            {
                                if (((SR.IObject)machine.Parent).ID == ((SR.IObject)executor).ID)
                                {
                                    // Add the children to this executor.
                                    nodeModel.Children.Add(new NodeViewModel(machine));
                                }
                            }
                        }

                        // Add the executor node to base model.
                        baseModel.Children.Add(nodeModel);
                    }
                }
                else
                {
                    TridentMessageBox.ShowTridentInformationMessageBox(Application.Current.MainWindow,
                        ManagementStudioResourceManager.GetString("NoNodesPresent"));
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
        }

        /// <summary>
        /// Update the center pane on selection of a item in the left pane.
        /// </summary>
        /// <param name="nodeModel">The selected node.</param>
        private void NodeSelectedCommandExecute(NodeViewModel nodeModel)
        {
            if (nodeModel != null)
            {
                IRegistryManagerCenterPanePresenter centerPanePresenter = RegistryManagerPresenterFactory.FetchCenterPanePresenter(typeof(NodeViewModel));
                centerPanePresenter.DeleteCommand = new DelegateCommand<BaseRegMgrModel>(this.OnDeleteNode);
                centerPanePresenter.SelectRootCommand = new DelegateCommand<object>(p => this.OnSelectRoot());
                (this.LeftPanePresenter as RegistryManagerLeftPanePresenter).ItemSelected.Execute(new object[] { centerPanePresenter, this, new SaveStateHandler(SaveStatus.Valid) });

                if (nodeModel.CompleteNodeViewModel)
                {
                    // Get the fresh instance of the node from the registry.
                    SR.Machine selectedMachine = this.registryManagerService.GetNode(nodeModel.Id);

                    // Update the model.
                    nodeModel.UpdateModel(selectedMachine);
                }

                if (!nodeModel.IsSelected)
                {
                    nodeModel.IsSelected = true;
                }

                centerPanePresenter.SelectedModel = nodeModel;
            }
        }

        /// <summary>
        /// Called when the center pane requests a root selection.
        /// </summary>
        private void OnSelectRoot()
        {
            if (this.baseModel != null)
            {
                this.baseModel.IsSelected = false;
                this.baseModel.IsSelected = true;
            }
        }

        /// <summary>
        /// Called when on delete node.
        /// </summary>
        /// <param name="model">The model.</param>
        private void OnDeleteNode(BaseRegMgrModel modelToDelete)
        {
            if (modelToDelete != null && this.baseModel != null)
            {
                NodeViewModel model = modelToDelete as NodeViewModel;
                if (model != null)
                {
                    int position = this.baseModel.Children.IndexOf(model);
                    if (position > -1)
                    {
                        this.baseModel.Children.Remove(model);
                    }
                }
            }
        }

        #endregion Methods
    }
}
